<?php

/**
 * @class UAL_Application_Router
 * @extends UAL_Router
 *
 * <p>A rendszer központi útvonalvezetékének aktuális első elemét használja a
 * felhasználó által keresett alkalmazás-osztály azonosítására. Az osztály
 * egyaránt használható főalkalmazás és alalkalmazás meghatározására - utóbbira
 * kizárólag abban az esetben, ha a konstruktornak átadtuk a szülőalkalmazás
 * példányát.</p>
 *
 * <p>Biztonsági okok miatt az osztályhoz tartozó konfigurációs állományban
 * kötelező felsorolni minden alkalmazást. Mivel az alkalmazások tetszőlegesen
 * egymásba ágyazhatóak (nem csak fában, hálóban is), fontos követni a
 * struktúrát. Például:
 * <code>
{
    ...
    "namespace": "Application",
    "applications": {
        "Common": "Common",
        "Admin": [
            "Edm",
            "DomainUser"
        ]
    }
}
</code>
 * A példában felsorolt alkalmazások osztályneveinek tehát a struktúrát kell
 * követnie, ami azt jelenti, hogy ilyen osztályú alkalmazásaink lesznek
 * (feltéve persze, ha "Admin" alkalmazás konfigurációjában a névstandardok az
 * ajánlott beállítási értéken vannak):
 * <ul>
 * <li>Application_Common</li>
 * <li>Application_Admin</li>
 * <li>Application_Admin_Case_Edm</li>
 * <li>Application_Admin_Case_DomainUser</li>
 * </ul>
 *
 * @author prometheus
 */
class UAL_Application_Router extends UAL_Router
{
	public $xName = 'Alkalmazásszintű útvonalkezelő';

	/**
	 * Az alkalmazás osztályok névterét tartalmazza.
	 * @var String
	 */
	protected $namespace = '';

	/**
	 * Az alapértelmezett alkalmazás elemneve (névtér nélkül).
	 * @var String
	 */
	protected $defaultApplication = '';

	/**
	 * A rendszerben regisztrált alkalmazások.
	 * @var Mixed
	 */
	protected $applications;

	/**
	 * A rendszerben regisztrált alkalmazások alapján készített alkalmazás-
	 * osztálylista.
	 * @var Array
	 */
	protected $appClassList = array();

	/**
	 * Szülő alkalmazás, amelyhez az útvonalválasztó tartozik.
	 * @var Task_Application
	 */
	protected $parentApplication;

	/**
	 * @var Task_Application
	 */
	protected $app;

	public function __construct(x10_Config $config=NULL, Task $parentApplication=NULL)
	{
		parent::__construct($config);

		$this->namespace = $this->config->getConfigKey('namespace');
		$this->applications = $this->config->getConfigKey('applications');
		$this->defaultApplication = $this->config->getConfigKey('defaultApplication');
		$this->parentApplication = $parentApplication;
		if (!$this->exists($this->defaultApplication))
		{
			throw new UAL_E_Application_Router_DefaultNotExists($this->defaultApplication);
		}
		$this->getList();
	}

	public function getClassNameFor($itemName)
	{
		if (is_null($this->parentApplication))
		{
			$result = $this->namespace.'_'.$itemName;
		}
		else
		{
			$result = $this->parentApplication->getStdAppClassName($itemName);
		}
		return $result;
	}

	/**
	 * Visszaadja a rendszerbe regisztrált alkalmazás osztályok listáját.
	 * Ennél valójában picit több jelentősége van ennek a metódusnak. Mivel a
	 * konstruktor automatikusan meghívja, már az első példányosításokkor átfut
	 * minden alkalmazás osztály az állapotkezelőn, így biztosítva, hogy az
	 * útvonalválasztás a leggyorsabban történhessen meg - mivel az
	 * állapotkezelő a példányosított osztályokat gyorsítótárazza.
	 * @return Array
	 */
	public function getList()
	{
		if (count($this->appClassList) > 0) return $this->appClassList;
		
		$this->listClassesRecursive($this->applications, $this->namespace, $this->appClassList);
		return $this->appClassList;
	}

	protected function listClassesRecursive($structure, $prefix, &$result)
	{
		$this->app = NULL;
		foreach ($structure as $k=>$v)
		{
			if (is_string($v))
			{
				$inserted = $prefix.'_'.$v;
				if ($prefix !== $this->namespace)
				{
					if (is_null($this->app))
					{
						if (!class_exists($prefix))
						{
							throw new UAL_E_Application_Router_RegisteredNotExists(str_replace('_', '.', $prefix));
						}
						$this->app = new $prefix();
					}
					$inserted = $this->app->getStdAppClassName($v);
				}
				$result[] = $inserted;
			}
			else if (is_object($v) or is_array($v))
			{
				$inserted = $prefix.'_'.$k;
				$result[] = $inserted;
				$this->listClassesRecursive($structure, $inserted, $result);
			}
		}
	}

	public function shiftQueue()
	{
		$queue = parent::shiftQueue();
		if (empty($queue))
		{
			$queue = $this->defaultApplication;
			$this->shifted = $queue;
		}
		return $queue;
	}

	public function handle()
	{
		$itemName = $this->shiftQueue();
		if (!empty($itemName))
		{
			$className = $this->getClassNameFor($itemName);
			if (!class_exists($className))
			{
				if ($this->mode == 'strict')
				{
					$app = new $className();
					$app->reportStatus('A keresett alkalmazás ("'.$itemName.'") nem található a rendszerben - sajnáljuk.', Application::HTTP_NOT_FOUND);
					return;
				}
				$itemName = $this->defaultApplication;
				$className = $this->getClassNameFor($this->defaultApplication);
				$this->cancelOperation();
			}
//		}
//		else
//		{
//			$itemName = $this->defaultApplication;
//			$className = $this->getClassNameFor($this->defaultApplication);
//		}

		$app = new $className();
		$app->getConfig()->setConfigKey('routerItemName', $itemName);
		return $app;
	}
}

?>